﻿namespace SuperModel
{
    using System;
    using System.ComponentModel;
    using System.Linq.Expressions;
    using System.Reflection;

    /// <summary>
    /// Base implementation of <see cref="INotifyPropertyChanged"/>
    /// </summary>
    public abstract class BaseNotifyPropertyChanged : INotifyPropertyChanged
    {
        /// <summary>
        /// Event for when a Property has Changed
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Gets a Property Info Object from a Given Expression
        /// </summary>
        /// <param name="expression">The expression.</param>
        /// <returns>The member expression</returns>
        protected static PropertyInfo GetPropertyInfo(Expression<Func<object>> expression)
        {
            var lambda = (LambdaExpression)expression;
            MemberExpression memberExpression;
            if (lambda.Body is UnaryExpression)
            {
                var unaryExpression = (UnaryExpression)lambda.Body;
                memberExpression = (MemberExpression)unaryExpression.Operand;
            }
            else
            {
                memberExpression = (MemberExpression)lambda.Body;
            }

            return (PropertyInfo)memberExpression.Member;
        }

        /// <summary>
        /// Perform Notification that a Property has Changed
        /// </summary>
        /// <param name="expression">The expression.</param>
        protected void Notify(Expression<Func<object>> expression)
        {
            var propertyInfo = GetPropertyInfo(expression);
            NotifyByName(propertyInfo.Name);
        }

        /// <summary>
        /// Perform Notification that a Property has Changed
        /// </summary>
        /// <param name="propertyName">
        /// The property Name.
        /// </param>
        protected virtual void NotifyByName(string propertyName)
        {
            FiryPropertyChanged(new PropertyChangedEventArgs(propertyName));
        }

        /// <summary>
        /// FiryPropertyChanged the Event Arguments
        /// </summary>
        /// <param name="args">
        /// The arguments.
        /// </param>
        private void FiryPropertyChanged(PropertyChangedEventArgs args)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, args);
            }
        }
    }
}
